use super::{Wheel, MAXIMUM_CAPACITY};
use chrono::Local;
use std::{
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc, Mutex,
    },
    thread,
};
use std::thread::JoinHandle;

#[derive(Debug)]
pub struct HasedWheel {
    cursor: Option<Arc<Cursor>>,
    start_time_ms: Option<i64>,
    tick_duration_ms: u32,
    tick_slot: u64,
    handle : Option<JoinHandle<()>>,
}

impl Wheel for HasedWheel {
    #[inline]
    fn new(tick_duration_ms: u32, mut tick_slot: u64) -> Self {
        tick_slot = format_size(tick_slot);
        Self {
            cursor: None,
            start_time_ms: None,
            tick_duration_ms,
            tick_slot,
            handle: None
        }
    }

    fn start<T>(&mut self, task: T) -> i64
    where
        T: Fn(i64) -> () + Send + 'static,
    {
        let cursor;
        if let Some(ref mut c) = self.cursor {
            if c.start.load(Ordering::Relaxed) {
                return self.start_time_ms.unwrap();
            }
            cursor = c.clone();
        } else {
            cursor = Arc::new(Cursor::new((self.tick_slot - 1) as i64, self.tick_duration_ms));
            self.cursor = Some(cursor.clone());
        }
        if let Ok(_) = cursor.start.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {
            let start_time_ms = Local::now().timestamp_millis();
            self.start_time_ms = Some(start_time_ms);
            let handle = thread::spawn(move || {
                let mut tick;
                let mut current_index;
                while cursor.start.load(Ordering::Acquire) {
                    tick = cursor.tick_tack(start_time_ms);
                    current_index = tick & cursor.mask;
                    task(current_index);
                }
            });
            self.handle = Some(handle);
            return start_time_ms;
        }
        return self.start_time_ms.unwrap();
    }

    fn stop(&mut self) {
        if let Some(ref mut c) = self.cursor {
            if c.start
                .compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed)
                .is_ok()
            {
                self.handle = None;
            }
        }
    }

    fn start_time_ms(&self) -> Option<i64> {
        self.start_time_ms
    }

    fn tick_duration_ms(&self) -> u32 {
        self.tick_duration_ms
    }

    fn tick_slot(&self) -> u64 {
        self.tick_slot
    }
}

/// 模仿指针转动
#[derive(Debug)]
struct Cursor {
    mask: i64,
    tick: Mutex<i64>,
    tick_duration_ms: u32,
    start: AtomicBool,
}

impl Cursor {
    fn new(mask: i64, tick_duration_ms: u32) -> Self {
        Self {
            mask,
            tick: Mutex::new(0),
            tick_duration_ms,
            start: AtomicBool::new(false),
        }
    }

    #[inline]
    fn tick_tack(&self, start_time_ms: i64) -> i64 {
        let re_tick;
        let next_time;
        {
            let mut tick = self.tick.lock().unwrap();
            re_tick = *tick;
            *tick = *tick + 1;
            // 获取下一个时间
            next_time = start_time_ms + re_tick * self.tick_duration_ms as i64;
        }
        let sleep_time = next_time - Local::now().timestamp_millis();
        if sleep_time > 0 {
            thread::sleep(std::time::Duration::from_millis(sleep_time as u64));
        }
        re_tick
    }
}

#[inline]
fn format_size(tick_solt: u64) -> u64 {
    let mut n = tick_solt - 1;
    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8;
    n |= n >> 16;
    if n >= MAXIMUM_CAPACITY {
        MAXIMUM_CAPACITY
    } else {
        n + 1
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use chrono::Local;

    #[test]
    fn test_wheel() {
        let mut wheel = HasedWheel::new(3, 1000);
        let start = wheel.start(|task| {
            println!("{:?} {:?}", Local::now(), task);
        });
        println!("wheel 启动时间 {}", start);
        while true {
            
        }
        // wheel.stop();
    }
}